import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

/**
 * The WorldGenerator class can create randomized worlds that simulate the kind that Joe might create.
 * 
 * TODO: This class is incomplete. Don't actually try to run it to make anything.
 * Everything is set except for the generateRandomReward method, but this might be replaced by doing an
 * actual analysis of the distribution from the test map, so I'm letting this sit for now.
 * 
 * @author adam
 *
 */
public class WorldGenerator
{
	private int worldCount;
	private String outputPath;
	
	/** square maps, so the x and y dimension of the world */
	private int dimension;
	
	public WorldGenerator(String outPath)
	{
		this.worldCount = 1;
		this.outputPath = outPath;
		this.dimension = 30;
	}
	
	///////////////////////////
	/// Getters and setters ///
	///////////////////////////

	public int getDimension()
	{
		return this.dimension;
	}
	
	public String getOutputPath()
	{
		return this.outputPath;
	}
	
	public int getWorldCount()
	{
		return this.worldCount;
	}
	
	public void setDimension(int dim)
	{
		this.dimension = dim;
	}
	
	public void setOutputPath(String outPath)
	{
		this.outputPath = outPath;
	}
	
	public void setWorldCount(int wCount)
	{
		this.worldCount = wCount;
	}
		
	///////////////////////////
	/// Real deal methods /////
	///////////////////////////
	
	/**
	 * This method generates a random value for reward at each cell. In order to figure out weights
	 * for each group, I made the following list from Joe's original example map1 (It's entirely possible
	 * I miscounted, but this is close enough).
	 * 
	 * >=-10 
	 * >-9
	 * >-8
	 * >-7
	 * >-6 ||
	 * >-5 ||
	 * >-4 |||
	 * >-3 ||||||||||||||||
	 * >-2 |||||||||||||||||||
	 * >-1 ||||||||||||||||||||||||||||||||||||
	 * <1 |||||||||||||
	 * <2 |||
	 * <3 |
	 * <4
	 * <5
	 * <6
	 * <7
	 * <8 |
	 * <9
	 * <=10
	 * -9999 ||||
	 * 
	 * Following this pattern, which is conveniently from a 10x10 world and so each occurence is 1%, we can
	 * roughly cluster these groups. Since some numbers were never hit, I deem them just as likely as one of
	 * the other relatively high numbers that only had a hit or two. Thus, I am creating the following clusters
	 */
	private float generateRandomReward()
	{
		float reward = 0f;
		
		return reward;
	}
	
	/**
	 * This method will make a sample world as described by assignment 2. Below is Joe's description
	 * of what the file will look like.
	 * 
	 * The first line contains an integer with the number of moves the agent has to reach a terminal state
	 * before the trial ends.
	 * The second line is the reward for failing to reach the goals in the number of required moves.
	 * The next ten lines of the file represent the agents starting positions for each of the agents runs
	 * through the maze (NB: this behavior is a change from the random start location discussed in lecture).
	 * The next line in the file is the number of terminal states.  Each terminal state will be on a separate
	 * line.  The bottom-left corner of the map is (1,1).  
	 * 
	 * @return The world we've just generated. Each element of the arraylist is a new line for a csv file
	 */
	public ArrayList<String> generateWorld()
	{
		ArrayList<String> world = new ArrayList<String>();
		
		// The number of moves the agent has to reach a terminal
		// I'm hardcoding this. What was the relative amount of moves to size of board/location of terminals?
		int movesAllowed = 54;
		world.add("" + movesAllowed);
		
		// Reward for failing to reach the goal
		// I'm not even sure that this is how no terminal reward is calcuated, but it's a possible pattern from the example
		float rewardForNoTerminal = (this.dimension * this.dimension) / 4;
		world.add("" + rewardForNoTerminal);
		
		// Ten starting positions
		// Don't know if these positions are truly random or not, but that's how this generation works
		for (int i = 0; i < 10; i++)
		{
			Random generator = new Random();
			int randomX = generator.nextInt(10) + 1;
			int randomY = generator.nextInt(10) + 1;
			world.add(randomX + "," + randomY);
		}
		
		// Number of terminals
		// Don't know how this is calculated, but this is a possibility from the example
		int terminalCount = this.dimension / 3;
		world.add("" + terminalCount);
		
		// Location of each terminal
		// Don't know if there's some sort of bias for where the terminals, but I'm doing pure random
		for (int i = 0; i < terminalCount; i ++)
		{
			Random generator = new Random();
			int randomX = generator.nextInt(10) + 1;
			int randomY = generator.nextInt(10) + 1;
			world.add(randomX + "," + randomY);
		}
		
		// Reward for each square
		// Don't know what the pattern is for rewards in different squares, but I made up a scheme
		for (int row = 0; row < this.dimension; row++)
		{
			String rowStr = ""; // each square value will be added to this string
			for (int col = 0; col < this.dimension; col++)
			{
				rowStr += generateRandomReward() + ",";
			}
			rowStr = rowStr.substring(rowStr.length() - 1); // trim off the last comma
			world.add(rowStr);
		}
		
		return world;
	}
	
	/**
	 * Given a particular world
	 */
	public void writeWorldToFile(ArrayList<String> world)
	{
		// Make a directory to house the directories of each world. Eventually each world folder will have map1-map10
		File worldPath = new File(this.outputPath + File.pathSeparator + "map" + this.worldCount);
		boolean mkDirSuccess = worldPath.mkdir();
		if (!mkDirSuccess)
		{ // if we're not going to be open the appropriate file, let's die really hard
			System.err.println("Couldn't make the directory for world " + this.worldCount + "\n\tAt filepath " + this.outputPath);
			System.exit(0);
		}
		
		try
		{
			// write this world as map1 to the world specific directory
			FileWriter fileWriter = new FileWriter(worldPath.getAbsoluteFile() + File.pathSeparator + "map1.txt");
			BufferedWriter writer = new BufferedWriter(fileWriter);
			
			for (int i = 0; i < world.size(); i++)
			{
				writer.write(world.get(i));
				writer.newLine();
			}
			
			writer.close();
		} catch (IOException e)
		{
			e.printStackTrace();
			System.exit(0);
		}
	}
	
	public static void main(String[] args)
	{
		WorldGenerator worldGenerator = new WorldGenerator("worlds");
		for (int i = 0; i < 10; i++)
		{
			ArrayList<String> world = worldGenerator.generateWorld(); // create the world
			worldGenerator.writeWorldToFile(world); // record the world
			worldGenerator.setWorldCount(worldGenerator.getWorldCount() + 1); // increment our count for naming purposes
		}
	}
}